package utils

import (
	"bytes"
	"crypto/rand"
	"errors"
	"fmt"
	"github.com/satori/go.uuid"
	"github.com/wonderivan/logger"
	"io/ioutil"
	"net"
	"os"
	"os/exec"
	"path/filepath"
	"regexp"
	"strconv"
	"strings"
	"text/template"
)

func ValidateSecret(in string) error {
	hasLower := regexp.MustCompile(`[a-z]`)
	hasUpper := regexp.MustCompile(`[A-Z]`)
	hasNumber := regexp.MustCompile(`[0-9]`)
	if len(in) >= 8 && hasLower.MatchString(in) && hasUpper.MatchString(in) && hasNumber.MatchString(in) {
		return nil
	}
	return errors.New("the password  must longer than 8 chars with at least 1 uppercase letter, 1 lowercase letter and 1 number")
}

func ValidateEmail(email string) error {

	if len(email) > 0 {
		if m, _ := regexp.MatchString(`^(([^<>()[\]\\.,;:\s@\"]+(\.[^<>()[\]\\.,;:\s@\"]+)*)|(\".+\"))@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$`, email); !m {
			return errors.New("Email with illegal format ")
		}
	} else {
		return errors.New("Email can't be empty ")
	}

	return nil
}

func ValidatePort(port string) error {

	if port == "" {
		return errors.New("port can't be empty ")
	}

	match, err := regexp.MatchString("^[0-9]*$", port)
	if err != nil {
		return fmt.Errorf("port %s with illegal format ", port)
	}

	i, err := strconv.Atoi(port)
	if err != nil {
		return fmt.Errorf("port %s with illegal format ", port)
	}
	if i < 0 || i > 65535 {
		return fmt.Errorf("port %s with illegal format ", port)
	}

	if match == false {
		return fmt.Errorf("port %s with illegal format ", port)
	}

	return nil
}

func ValidateIP(ip string) error {

	if len(ip) > 0 {
		if a := net.ParseIP(ip); a == nil {
			return fmt.Errorf("IP %s with illegal format ", ip)
		}
	} else {
		return errors.New("IP can't be empty ")
	}

	return nil
}
func ValidateCIDR(ipCIDR string) error {

	if len(ipCIDR) > 0 {
		if _, _, err := net.ParseCIDR(ipCIDR); err != nil {
			return fmt.Errorf("CIDR IP %s with illegal format ", ipCIDR)
		}
	} else {
		return fmt.Errorf("CIDR IP can't be empty")
	}

	return nil
}

// IsIllegalLength ...
func IsIllegalLength(s string, min int, max int) bool {
	if min == -1 {
		return len(s) > max
	}
	if max == -1 {
		return len(s) <= min
	}
	return len(s) < min || len(s) > max
}

// IsContainIllegalChar ...
func IsContainIllegalChar(s string, illegalChar []string) bool {
	for _, c := range illegalChar {
		if strings.Index(s, c) >= 0 {
			return true
		}
	}
	return false
}

func GenerateRandomString() string {
	length := 32
	const chars = "abcdefghijklmnopqrstuvwxyz0123456789"
	l := len(chars)
	result := make([]byte, length)
	rand.Read(result)

	for i := 0; i < length; i++ {
		result[i] = chars[int(result[i])%l]
	}
	return string(result)
}

func GenUUID() string {
	id := uuid.NewV4()
	return id.String()

}

func Exists(path string) bool {

	_, err := os.Stat(path)
	if err != nil {
		if os.IsExist(err) {
			return true
		}
		return false
	}
	return true
}

func FromLocal(localPath string) string {
	cmd := fmt.Sprintf("md5sum %s | cut -d\" \" -f1", localPath)
	c := exec.Command("sh", "-c", cmd)
	out, err := c.CombinedOutput()
	if err != nil {
		logger.Error(err)
	}
	md5 := string(out)
	md5 = strings.ReplaceAll(md5, "\n", "")
	md5 = strings.ReplaceAll(md5, "\r", "")

	return md5
}

func ReadFile(name string) string {
	content, err := ioutil.ReadFile(name)
	if err != nil {
		logger.Error("[globals] read file err is : %s", err)
		os.Exit(1)
	}
	return string(content)
}

func MkDir(path string) {
	if _, err := os.Stat(path); os.IsNotExist(err) {
		os.MkdirAll(path, os.ModePerm)
	}
}

func RemoveFile(path string) {
	if _, err := os.Stat(path); os.IsExist(err) {
		os.Remove(path)
	}
}

func WriteFile(filePath string, fileBytes []byte) error {

	// 写文件
	newFile, err := os.Create(filePath)
	if err != nil {
		return err
	}
	defer newFile.Close()

	if _, err := newFile.Write(fileBytes); err != nil || newFile.Close() != nil {
		return err
	}

	return nil
}

func GetFileName(dir string) ([]string, error) {
	var result []string
	if _, err := os.Stat(dir); os.IsExist(err) {
		return nil, err
	}

	files, _ := ioutil.ReadDir(dir)
	for _, f := range files {

		result = append(result, f.Name())
	}
	return result, nil
}

func GetFileFullPath(dir string) ([]string, error) {
	if _, err := os.Stat(dir); os.IsExist(err) {
		return nil, err
	}

	filepathNames, err := filepath.Glob(filepath.Join(dir, "*"))
	if err != nil {
		return nil, err
	}

	return filepathNames, nil
}

func Render(tpl string, data interface{}) string {
	var b bytes.Buffer
	t := template.Must(template.New("text").Parse(tpl))
	t.Execute(&b, &data)
	return b.String()
}


